<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="yW0sj" id="yW0sj"><span data-lake-id="u5e2eaa41" id="u5e2eaa41">典型回答</span></h1>
  <p data-lake-id="u8d536a3f" id="u8d536a3f"><br></p>
  <p data-lake-id="u302f6729" id="u302f6729"><span data-lake-id="u6878e391" id="u6878e391">Java中的Timer类是一个定时调度器，用于在指定的时间点执行任务。JDK 中Timer类的定义如下：</span></p>
  <p data-lake-id="ufc73d4f6" id="ufc73d4f6"><span data-lake-id="u7c1f5b5d" id="u7c1f5b5d">​</span><br></p>
  <pre lang="java"><code>
public class Timer {
    /**
     * The timer task queue.  This data structure is shared with the timer
     * thread.  The timer produces tasks, via its various schedule calls,
     * and the timer thread consumes, executing timer tasks as appropriate,
     * and removing them from the queue when they're obsolete.
     */
    private final TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.
     */
    private final TimerThread thread = new TimerThread(queue);
}
</code></pre>
  <p data-lake-id="u43eb956b" id="u43eb956b"><br></p>
  <p data-lake-id="u53131b8c" id="u53131b8c"><span data-lake-id="ud4c99db4" id="ud4c99db4">以上就是Timer中最重要的两个成员变量：</span></p>
  <p data-lake-id="u3dfbbced" id="u3dfbbced"><span data-lake-id="u371cb3d0" id="u371cb3d0">​</span><br></p>
  <ol list="u7662892a">
   <li fid="u94c5f30c" data-lake-id="u32a37186" id="u32a37186"><span data-lake-id="u9f8d645a" id="u9f8d645a">TaskQueue：一个任务队列，用于存储已计划的定时任务。任务队列按照任务的执行时间进行排序，确保最早执行的任务排在队列前面。在队列中的任务可能是一次性的，也可能是周期性的。</span></li>
   <li fid="u94c5f30c" data-lake-id="ueefbdfc0" id="ueefbdfc0"><span data-lake-id="u0d858c3b" id="u0d858c3b">TimerThread：Timer 内部的后台线程，它负责扫描 TaskQueue 中的任务，检查任务的执行时间，然后在执行时间到达时执行任务的 run() 方法。TimerThread 是一个守护线程，因此当所有非守护线程完成时，它会随之终止。</span></li>
  </ol>
  <p data-lake-id="udfb2f0b9" id="udfb2f0b9"><span data-lake-id="u8d1d9263" id="u8d1d9263">​</span><br></p>
  <p data-lake-id="ub6a6a092" id="ub6a6a092"><span data-lake-id="ueea24739" id="ueea24739">​</span><br></p>
  <p data-lake-id="uba68b84a" id="uba68b84a"><span data-lake-id="u77ef0e1e" id="u77ef0e1e">任务的定时调度的核心代码就在TimerThread中：</span></p>
  <p data-lake-id="ud5db8c2c" id="ud5db8c2c"><span data-lake-id="ub5920f42" id="ub5920f42">​</span><br></p>
  <pre lang="java"><code>
class TimerThread extends Thread {
   
    boolean newTasksMayBeScheduled = true;

    /**
     * 存储 TimerTask 的队列
     */
    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            mainLoop();
        } finally {
            synchronized (queue) {
                newTasksMayBeScheduled = false;
                queue.clear(); 
            }
        }
    }

    /**
     * 主要的计时器循环。 
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized (queue) {
                    // 等待队列变为非空
                    while (queue.isEmpty() &amp;&amp; newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // 队列为空，将永远保持为空；线程终止

                    // 队列非空；查看第一个事件并执行相应操作
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized (task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // 无需执行任何操作，再次轮询队列
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime &lt;= currentTime)) {
                            if (task.period == 0) { // 非重复，移除
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // 重复任务，重新安排
                                queue.rescheduleMin(
                                  task.period &lt; 0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // 任务尚未触发；等待
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // 任务触发；运行它，不持有锁
                    task.run();
            } catch (InterruptedException e) {
            }
        }
    }
}

</code></pre>
  <p data-lake-id="u230c5bfc" id="u230c5bfc"><br></p>
  <p data-lake-id="u3ec1de44" id="u3ec1de44"><span data-lake-id="u17a8a55d" id="u17a8a55d">可以看到，TimerThread的实际是在运行mainLoop方法，这个方法一进来就是一个while(true)的循环，他在循环中不断地从TaskQueue中取出第一个任务，然后判断他是否到达执行时间了，如果到了，就触发任务执行。否则就继续等一会再次执行。</span></p>
  <p data-lake-id="ud8dea165" id="ud8dea165"><span data-lake-id="ud93fe50d" id="ud93fe50d">​</span><br></p>
  <p data-lake-id="u954a4389" id="u954a4389"><span data-lake-id="uf1284012" id="uf1284012">不断地重复这个动作，从队列中取出第一个任务进行判断，执行。。。</span></p>
  <p data-lake-id="u552de408" id="u552de408"><span data-lake-id="u6350011b" id="u6350011b">​</span><br></p>
  <p data-lake-id="u8192242b" id="u8192242b"><span data-lake-id="ube14fc8f" id="ube14fc8f">这样只要有新的任务加入队列，就在队列中按照时间排序，然后唤醒timerThread重新检查队列进行执行就可以了。代码如下：</span></p>
  <p data-lake-id="u89740f41" id="u89740f41"><span data-lake-id="u38da29d5" id="u38da29d5">​</span><br></p>
  <pre lang="java"><code>
    private void sched(TimerTask task, long time, long period) {
        if (time &lt; 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // Constrain value of period sufficiently to prevent numeric
        // overflow while still being effectively infinitely large.
        if (Math.abs(period) &gt; (Long.MAX_VALUE &gt;&gt; 1))
            period &gt;&gt;= 1;

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }
        	//新任务入队列
            queue.add(task);
            //唤醒任务
            if (queue.getMin() == task)
                queue.notify();
        }
    }
</code></pre>
  <p data-lake-id="u11576118" id="u11576118"><span data-lake-id="ue418afc6" id="ue418afc6">​</span><br></p>
  <h1 data-lake-id="XNqoP" id="XNqoP"><span data-lake-id="ube36ef12" id="ube36ef12">扩展知识</span></h1>
  <p data-lake-id="ud81ea0ba" id="ud81ea0ba"><br></p>
  <h2 data-lake-id="eEOOj" id="eEOOj"><span data-lake-id="u311cf103" id="u311cf103">优缺点</span></h2>
  <p data-lake-id="ue6e77ad1" id="ue6e77ad1"><br></p>
  <p data-lake-id="uf7cf801b" id="uf7cf801b"><span data-lake-id="u01674c62" id="u01674c62">Timer 类用于实现定时任务，最大的好处就是他的实现非常简单，特别的轻量级，因为它是Java内置的，所以只需要简单调用就行了。</span></p>
  <p data-lake-id="u4436cebf" id="u4436cebf"><span data-lake-id="u6a7988a0" id="u6a7988a0">​</span><br></p>
  <p data-lake-id="u018c9df1" id="u018c9df1"><span data-lake-id="uc0cf11fa" id="uc0cf11fa">但是他并不是特别的解决定时任务的好的方案，因为他存在以下问题：</span></p>
  <p data-lake-id="ucd4cdcc6" id="ucd4cdcc6"><span data-lake-id="ua295502b" id="ua295502b">​</span><br></p>
  <p data-lake-id="u4e01c0c9" id="u4e01c0c9"><span data-lake-id="ud8fa2230" id="ud8fa2230">1、Timer内部是单线程执行任务的，如果某个任务执行时间较长，会影响后续任务的执行。</span></p>
  <p data-lake-id="u94bd8b93" id="u94bd8b93"><span data-lake-id="udcff61cf" id="udcff61cf">2、如果任务抛出未捕获异常，将导致整个 Timer 线程终止，影响其他任务的执行。</span></p>
  <p data-lake-id="ua61f2ce5" id="ua61f2ce5"><span data-lake-id="u85fc96ac" id="u85fc96ac">3、Timer 无法提供高精度的定时任务。因为系统调度和任务执行时间的不确定性，可能导致任务执行的时间不准确。</span></p>
  <p data-lake-id="ue89ad016" id="ue89ad016"><span data-lake-id="u2a09f42c" id="u2a09f42c">4、虽然可以使用 cancel 方法取消任务，但这仅仅是将任务标记为取消状态，仍然会在任务队列中占用位置，无法释放资源。这可能导致内存泄漏。</span></p>
  <p data-lake-id="u9d9105f7" id="u9d9105f7"><span data-lake-id="u545c08bf" id="u545c08bf">5、当有大量任务时，Timer 的性能可能受到影响，因为它在每次扫描任务队列时都要进行时间比较。</span></p>
  <p data-lake-id="ua8c6555d" id="ua8c6555d"><span data-lake-id="udfc4d744" id="udfc4d744">6、Timer执行任务完全基于JVM内存，一旦应用重启，那么队列中的任务就都没有了</span></p>
  <p data-lake-id="u82b3bad7" id="u82b3bad7"><span data-lake-id="u2ef48b9f" id="u2ef48b9f"><br></span></p>
 </body>
</html>